package main

import (
	"fmt"
	"runtime/debug"
	"sync"

	"gitee.com/nggs/protoactor-go/actor"

	myactor "gitee.com/nggs/actor"
	msexport "gitee.com/nggs/microservice/export"
	msservice "gitee.com/nggs/microservice/service"
)

const serviceName = "service"

func init() {
	msservice.FM.Register(serviceName, func() msexport.IService { return newService() })
}

type service struct {
	myactor.ISuper

	entityPIDByID   map[int32]*actor.PID // entity id -> arena pid
	entityStartedWg sync.WaitGroup
	entityStoppedWg sync.WaitGroup
}

func newService() *service {
	svc := &service{
		entityPIDByID: map[int32]*actor.PID{},
	}
	return svc
}

func (svc *service) Init(id int, startedWg *sync.WaitGroup, stoppedWg *sync.WaitGroup, args ...interface{}) (err error) {
	svc.ISuper = myactor.NewSuper(nil, startedWg, stoppedWg).
		WithOnReceiveMessageHandler(svc.onReceiveMessage).
		WithOnStartedHandler(svc.onStarted).
		WithOnStoppingHandler(svc.onStopping).
		WithOnStoppedHandler(svc.onStopped).
		WithOnActorTerminateHandler(svc.onActorTerminated)
	return
}

func (svc *service) Run(ctx actor.Context, pprofAddr string, args ...interface{}) (err error) {
	err = svc.ISuper.Start(ctx, fmt.Sprintf("%s-%d", serviceName, 1))
	if err != nil {
		return
	}
	return
}

func (svc *service) onStarted(ctx actor.Context) {
	for i := 1; i <= 20000; i++ {
		e := newEntity(int32(i), &svc.entityStartedWg, &svc.entityStoppedWg)
		if err := e.Run(ctx); err != nil {
			continue
		}
		svc.entityPIDByID[int32(i)] = e.PID().Clone()
	}
	svc.Debug("onStarted, len(svc.entityPIDByID)=%d", len(svc.entityPIDByID))
}

func (svc *service) onStopping(ctx actor.Context) {
	svc.Debug("onStopping")
}

func (svc *service) onStopped(ctx actor.Context) {
	svc.Debug("onStopped, len(svc.entityPIDByID)=%d", len(svc.entityPIDByID))

	if len(svc.entityPIDByID) != 0 {
		svc.Error("entityPIDByID not empty")
	}
}

func (svc *service) onReceiveMessage(ctx actor.Context) {
	defer func() {
		if r := recover(); r != nil {
			svc.Error("%v\n%s", r, debug.Stack())
			panic(r)
		}
	}()

	sender := ctx.Sender()
	switch msg := ctx.Message().(type) {
	default:
		svc.Error("recv unsupported msg [%#v] from [%v]", msg, sender)
	}
}

func (svc *service) onActorTerminated(who *actor.PID, ctx actor.Context) {
	//svc.Debug("[%s] terminated", who.Id)

	var entityID int32
	if _, err := fmt.Sscanf(who.Id, "p-1/service-1/entity-%d", &entityID); err == nil {
		delete(svc.entityPIDByID, entityID)
	}
}
